#include "Pch.h"

#include "RayTraceEngine.h"
#include "Light.h"
#include "RayCastEngine.h"

namespace  IS
{

class SimpleRayTraceEngine::Impl
{
public:
	LinkList<Light> mLights;
};

SimpleRayTraceEngine::SimpleRayTraceEngine()
{
	mImpl = new Impl;
}

SimpleRayTraceEngine::~SimpleRayTraceEngine()
{
	delete mImpl;
}

void SimpleRayTraceEngine::beginAddLight()
{
}

void SimpleRayTraceEngine::addLight(Light* light)
{
	mImpl->mLights.pushBack(*light);
}

void SimpleRayTraceEngine::endAddLight()
{
}

void SimpleRayTraceEngine::bake(IRayCastEngine* rcEngine, LinkList<Sample>* samplesToBake)
{
	// reset samples illuminations
	for(Sample* sample = samplesToBake->begin()
		; samplesToBake->end() != sample
		; sample = sample->next())
	{
		sample->illumination = Vec3f(0, 0, 0);
	}

	// ray trace for each light
	for(Light* lt = mImpl->mLights.begin()
		; mImpl->mLights.end() != lt
		; lt = lt->next())
	{
		// remember to call beginRayTracing()!
		lt->beginRayTracing();
		
		// pass 1, compute visibility to this light

		// issue ray-cast operations
		rcEngine->beginRayCast();

		for(Sample* sample = samplesToBake->begin()
			; samplesToBake->end() != sample
			; sample = sample->next())
		{
			Ray ray;
			lt->getVisibilityRay(sample->position, ray);
			rcEngine->rayCast(ray);
		}

		// retrieve ray-cast results
		LinkList<RayCastResult>* results = rcEngine->endRayCast();
		RayCastResult* result = results->begin();

		for(Sample* sample = samplesToBake->begin()
			; samplesToBake->end() != sample
			; sample = sample->next())
		{
			MCD_ASSERT(sample);
			MCD_ASSERT(result);

			sample->reserved[0].x = result->hit ? 0.0f : 1.0f;

			result = result->next();
		}

		delete results;
		
		// pass 2, compute illumination
		for(Sample* sample = samplesToBake->begin()
			; samplesToBake->end() != sample
			; sample = sample->next())
		{
			if (sample->reserved[0].x < 1.0f)
				continue;

			Vec3f diffuse;
			lt->getDiffuse(sample->position, sample->normal, diffuse);

			sample->illumination += diffuse;
		}

		// remember to call endRayTracing()!
		lt->endRayTracing();
	}
}

}